//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <mantle.h>
#include <ckernel.h>
#include <_schedule.h>

extern CKernel * g_pCKernel; // define in crust/kmarshal/kmarshal.cpp

static Int32 PoolThreadEntry(void *pvArg)
{
    GetCurrentThread()->ReadyToStart();
    return 0;
}

ECode ThreadPool::CreateThread(
    Thread **ppThread,
    ScheduleClass *pScheduleClass,
    uint_t uSchedulePolicy)
{
    Thread *pThread;
    ECode ec;

    assert(ppThread);
    pThread = ::CreateThread(
        m_pOwner,
        &PoolThreadEntry,
        NULL,
        pScheduleClass,
        uSchedulePolicy);
    if (NULL == pThread) return E_OUT_OF_MEMORY;

    //
    // Allocate user stack for the created thread.
    //
    ec = m_pOwner->m_AS.AllocThreadStack(&pThread->m_uUserStackBase);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    assert(!DzIsSystemThread(pThread));
    *ppThread = pThread;
    return NOERROR;

ErrorExit:
    pThread->Abort(m_pOwner);
    // NO need FreeThread here
    // Abort will cause thread ref == 0, AND FreeThread.
    return ec;
}

ECode ThreadPool::CreateKernelThread(
    threadroutine_t threadroutine,
    void *pArg,
    UInt32 dwFlags,
    ScheduleClass *pScheduleClass,
    uint_t uSchedulePolicy,
    IThread **ppThread)
{
    Thread *pThread;
    ECode ec;

    assert(NULL != ppThread);

    ec = this->GetThread(&pThread, pScheduleClass, uSchedulePolicy);
    if (FAILED(ec)) return ec;

    pThread->SetKernelEntry(threadroutine, pArg);

    assert(!DzIsSystemThread(pThread));

    *ppThread = pThread;

    return NOERROR;
}

void ThreadPool::DestroyThread(Thread *pThread)
{
    UInt32 uStackBase = InterlockedExchange(
                        (Int32 *)&pThread->m_uUserStackBase, 0);
    if (uStackBase) {
        m_pOwner->m_AS.Unmap(uStackBase, USER_STACK_SIZE);
    }

    pThread->Abort(m_pOwner);
}

ECode ThreadPool::GetThread(
    Thread **ppThread,
    ScheduleClass *pScheduleClass,
    uint_t uSchedulePolicy)
{
    Thread *pThread;
    ECode ec;

    assert(ppThread);

    m_threadLock.Lock();

    if (ProcessState_Finished == m_pOwner->m_processState &&
        m_pOwner != ::GetCurrentProcess()) {
        m_threadLock.Unlock();
        return E_PROCESS_ALREADY_EXITED;
    }

    pThread = FindThread(pScheduleClass, uSchedulePolicy);
    if (NULL != pThread) {
        //
        // Get a free thread from pool.
        //
        m_cThreads--;
        pThread->m_inProcLink.Detach();
        // for detach twice.(In thread release)
        pThread->m_inProcLink.Initialize();
        assert(pThread->m_pOwner == m_pOwner);
        m_threadLock.Unlock();
    }
    else {
        //
        // There are no threads left in pool, create new one.
        //
        ec = this->CreateThread(&pThread, pScheduleClass, uSchedulePolicy);

        m_threadLock.Unlock();

        if (FAILED(ec)) return ec;
    }
    assert(pThread->m_fnKernelEntry == NULL);
    assert(pThread->m_uPoolThreadState == ThreadState_Unstarted
        || pThread->m_bHasUserEntry == FALSE);
    assert(pThread->m_uPoolThreadState == ThreadState_Unstarted);

    pThread->AddRef();
    *ppThread = pThread;
    return NOERROR;
}

void ThreadPool::PutBackThread(Thread *pThread)
{
    assert(pThread->m_uPoolThreadState == ThreadState_Unstarted
        || pThread->m_uPoolThreadState == ThreadState_Finished);
    assert(pThread->m_fnKernelEntry == NULL);
    assert(pThread->m_uPoolThreadState == ThreadState_Unstarted
        || pThread->m_bHasUserEntry == FALSE);
    assert(pThread->m_pOwner != NULL);

    ECode ec;

    m_threadLock.Lock();
    if (m_cThreads < m_uCapacity) {
        //
        // Put back the current thread to pool.
        //

        //
        // Decrease user stack before put back to pool.
        //
        assert(pThread->m_uUserStackBase);
        ec = m_pOwner->m_AS.DecreaseThreadStack(
                pThread->m_uUserStackBase);
        if (FAILED(ec)) {
            goto ErrorExit;
        }

        pThread->m_bPendingInterrupt = FALSE;
        pThread->m_bInterrupted = FALSE;

        pThread->m_uUserProcessorTicks       = 0;
        pThread->m_uPrivilegedProcessorTicks = 0;

		pThread->m_pScheduleClass->InitializeThread(pThread);

        memset(pThread->m_pvFields, 0, TF_MINIMUM_AVAILABLE * sizeof(void *));

        pThread->m_exitCode = E_THREAD_ABORTED;

        *(pThread->m_wszName) = 0;
        pThread->ReallocBuffer(0);

        if (ThreadState_Unstarted != pThread->m_uPoolThreadState) {
            assert(pThread->m_pOwner == m_pOwner);
            if (0 == m_pOwner->Release(pThread)) {
                if (pThread == ::GetCurrentThread()) {
                    ExitThread(NULL);
                    assert(0);
                }
                else {
                    // Don't unlock m_threadLock.
                    GetCurrentThread()->m_activeLock._Unlock();
                    return;
                }
            }
            pThread->m_uPoolThreadState = ThreadState_Unstarted;
        }

        //
        // Insert to pool thread list.
        //
        m_threadList.InsertPrev(&(pThread->m_inProcLink));
        m_cThreads++;

        m_threadLock.Unlock();
    }
    else {
        m_threadLock.Unlock();

        //
        // The thread pool has overloaded, destroy this thread.
        //
        this->DestroyThread(pThread);
    }

    return;

ErrorExit:
    m_threadLock.Unlock();
    this->DestroyThread(pThread);
    return;
}

ThreadPool::~ThreadPool()
{
    Thread *pThread;

    m_threadLock.Lock();

    while (!m_threadList.IsEmpty()) {
        pThread = THIS(
            (InProcessLinkNode *)m_threadList.m_pNext, Thread, m_inProcLink);
        pThread->m_inProcLink.Detach();
        // for detach twice.(In thread release)
        pThread->m_inProcLink.Initialize();
        if (::GetCurrentThread() == pThread) continue;
        assert(pThread->m_uPoolThreadState != ThreadState_Running);
        pThread->m_pOwner = NULL;
        pThread->Abort(NULL);
    }
    m_threadLock.Unlock();
}
